package com.dny.asmtop.op;

import com.dny.asmtop.MethodContext;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static java.util.Collections.EMPTY_LIST;
import static jdk.internal.org.objectweb.asm.Type.BOOLEAN_TYPE;

/**
 * Created by jlutt on 2018-01-16.
 * bool and表达
 * @author jlutt
 */
public class PredicateDefAnd implements PredicateDef {

  private final List<PredicateDef> predicates;

  private PredicateDefAnd(List<PredicateDef> predicates) {
    this.predicates = new ArrayList<>(predicates);
  }

  public static PredicateDefAnd create() {
    return new PredicateDefAnd(EMPTY_LIST);
  }

  public static PredicateDefAnd create(List<PredicateDef> predicates) {
    return new PredicateDefAnd(predicates);
  }

  public PredicateDefAnd add(PredicateDef predicateDef) {
    this.predicates.add(predicateDef);
    return this;
  }

  @Override
  public Type type(MethodContext context) {
    return BOOLEAN_TYPE;
  }

  @Override
  public Type generator(MethodContext context) {
    GeneratorAdapter g = context.getGeneratorAdapter();
    Label exit = new Label();
    Label labelFalse = new Label();
    for (PredicateDef predicate : predicates) {
      Type type = predicate.generator(context);
      assert type == BOOLEAN_TYPE;
      g.ifZCmp(GeneratorAdapter.EQ, labelFalse);
    }
    g.push(true);
    g.goTo(exit);

    g.mark(labelFalse);
    g.push(false);

    g.mark(exit);
    return BOOLEAN_TYPE;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    PredicateDefAnd that = (PredicateDefAnd) o;
    return Objects.equals(predicates, that.predicates);
  }

  @Override
  public int hashCode() {
    return Objects.hash(predicates);
  }
}
